package com.lap.auth.filter;

import com.lap.auth.application.authentication.AuthQueryService;
import com.lap.auth.application.authentication.dto.UserDto;
import com.lap.auth.shared.errors.UserError;
import com.lap.context.starter.core.helper.MessageHelper;
import com.lap.framework.common.constant.Header;
import com.lap.framework.common.dto.result.Result;
import com.lap.framework.common.exception.BizException;
import com.lap.framework.common.tool.JsonUtil;
import io.micrometer.common.util.StringUtils;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Optional;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * 鉴权拦截器
 *
 * @author Shuisheng Lao(劳水生)
 * @version 0.0.1
 */
@Slf4j
@RequiredArgsConstructor
public class AuthenticationFilter extends OncePerRequestFilter {
  private static final String AUTHORIZATION = "Authorization";
  public static final String BEARER = "Bearer ";

  private final Security security = new Security();
  private final AuthQueryService authQueryService;
  private final MessageHelper messageHelder;

  @Override
  protected void doFilterInternal(
      HttpServletRequest request,
      @NonNull HttpServletResponse response,
      @NonNull FilterChain filterChain)
      throws ServletException, IOException {

    boolean ignoreToken = security.ignoreToken(request.getRequestURI(), request.getMethod());
    if (ignoreToken) {
      filterChain.doFilter(request, response);
      return;
    }

    String token = resolveToken(request);
    if (StringUtils.isEmpty(token)) {
      log.warn("Token is empty");
      print(response, Result.fail(UserError.NO_AUTH, "Token is empty"));
      return;
    }

    try {
      UserDto dto = authQueryService.validateToken(token);
      HeaderModifyRequestWrapper wrapper = new HeaderModifyRequestWrapper(request);
      wrapper.putHeader(Header.USER_ID, String.valueOf(dto.getUserId()));
      wrapper.putHeader(Header.USER_NAME, dto.getUserName());
      wrapper.putHeader(Header.FULL_NAME, dto.getFullName());

      filterChain.doFilter(wrapper, response);
    } catch (BizException e) {
      String msg = messageHelder.getMsg(e.getErrorCode().getMessageKey(), e.getArgs());
      Result<?> result = Result.fail(e.getErrorCode(), msg);
      print(response, result);
    } catch (Exception e) {
      log.error(e.getMessage(), e);
    }
  }

  private String resolveToken(HttpServletRequest request) {
    String token =
        Optional.ofNullable(request.getHeader(AUTHORIZATION))
            .orElse(request.getParameter(AUTHORIZATION));

    return Optional.ofNullable(token)
        .filter(e -> e.contains(BEARER))
        .map(e -> e.substring(BEARER.length()))
        .orElse(token);
  }

  private void print(HttpServletResponse response, Result<?> result) {
    try (PrintWriter writer = response.getWriter(); ) {
      writer.write(JsonUtil.toJson(result));
    } catch (IOException e) {
      log.error(e.getMessage(), e);
    }
  }
}
